import enum
import common.globaldefines
from . import actionmanager
import application
import time
import copy
import json

g_FlowManager = None

class ExecuteStage(enum.Enum):

    Execute = 0
    ExecuteFlow = 1
    CheckResult = 2
    CheckSubFlowResult = 3
    Finished = 10

class ExecuteNode:

    def __init__(self, parent, uuid, info: dict):
        self._parent = parent
        self._uuid = uuid
        self._out = info.get("Out", "")
        self._type = info.get("Type", "")
        self._action = info.get("Action", "")
        self._true = info.get("True", "")
        self._false = info.get("False", "")
        self._break = info.get("Break", "")
        self._pos = info.get("Pos", [0, 0])

        self._flowobj = None
        self._outnode = None
        self._truenode = None
        self._falsenode = None

        self._begin: bool = False

    def getID(self):
        return self._uuid

    def isStarted(self):
        return self._begin

    def start(self):
        self._begin = True
        self.debugPrint(f"[Node start] {[self._type]}{[self._uuid]}{[self._action]}")
        self.onStarted()

    def onStarted(self):
        pass

    def end(self):
        self.debugPrint(f"[Node end] {[self._type]}{[self._uuid]}{[self._action]}")
        self.onEnd()

    def onEnd(self):
        pass

    def debugDebugPrint(self, *args):
        DebugPrint("    ", *args)

    def init(self):
        self._outnode = self._parent.getNode(self._out)
        self._truenode = self._parent.getNode(self._true)
        self._falsenode = self._parent.getNode(self._false)

    def isFlowNode(self):
        if "flow" == self._action[:len("flow")]:
            return True
        return False
    
    def isActionNode(self):
        if self._action and not self.isFlowNode():
            return True
        return False

    def isFlowFinished(self):
        flowobj: FlowBase = self._flowobj
        if not flowobj:
            DebugPrint("?????????????")
            return False
        return flowobj.isFinished()

    def execute(self):
        #self.debugDebugPrint(f"node {self._uuid} action: \"{self._action}\", execute.")
        return self.onExecute()

    def onExecute(self):
        if self.isFlowNode():
            return self.executeFlow()
        elif self.isActionNode():
            return self.executeAction()
        else:
            return self._outnode
    
    def executeFlow(self):
        if not self._flowobj:
            flowobj = g_FlowManager.getFlow(self._action)
            self._flowobj = flowobj.create()
        if not self._flowobj:
            self.debugPrint(f"flow id: \"{self._action}\" not found, no execute. default True")
            r = True
        else:
            r = self._flowobj.execute()
        if r:
            self.debugPrint(f"{[self._type]}{[self._uuid]}{[self._flowobj.getName()]} return , next: {self._out}, {r}")
            return self._outnode
        else:
            # 没执行完就继续执行
            return self

    def executeAction(self):
        actobj = actionmanager.g_ActMgr.GetAction(self._action)
        if not actobj:
            self.debugPrint(f"action id: \"{self._action}\" not found, no execute. default True")
            r = True
        else:
            if "N_IF" == self._type:
                r = actobj.Check()
            else:
                r = actobj.Execute()
        if "N_IF" == self._type:
            if r:
                self.debugPrint(f"{[self._type]}{[self._uuid]}{[actobj.GetName()]} return true, next: {self._true}")
                return self._truenode
            else:
                self.debugPrint(f"{[self._type]}{[self._uuid]}{[actobj.GetName()]} return false, next: {self._false}")
                return self._falsenode
        else:
            self.debugPrint(f"{[self._type]}{[self._uuid]}{[actobj.GetName()]} return , next: {self._out}")
            return self._outnode
        
    def reset(self):
        self._begin = False
        self.onReset()

    def onReset(self):
        pass

class Node_Sleep(ExecuteNode):

    _name = "Sleep"

    def __init__(self, parent, uuid, info):
        super().__init__(parent, uuid, info)
        self._endtime = 0
        self._delay = info.get("Param", 0.1)

    def onExecute(self):
        if not self._endtime:
            self.debugPrint(f"sleep begin. {self._delay}(s)")
            self._endtime = time.time() + self._delay
            return self
        curtime = time.time()
        if curtime < self._endtime:
            return self
        # 时间够了就下一个节点
        self.debugPrint(f"sleep {self._delay}(s) end. {self._out} {self._outnode}")
        return self._outnode

class Node_For(ExecuteNode):

    _name = "For"

    def __init__(self, parent, uuid, info):
        super().__init__(parent, uuid, info)
        self._times = info.get("Param", 1)

    def onStarted(self):
        self.debugPrint(f"loop last times: {self._times}")

    def onExecute(self):
        node = super().onExecute()
        DebugPrint(node, "<<<<<<<<<<<<")
        if self.isFlowNode():
            if self != node: # 返回自身说明需要继续执行，返回不是自身就说明执行完成
                self._times -= 1
                self.debugPrint(f"loop last times: {self._times}")
                # 重置这个node，否则会被判断为finished.不符合期望
                if self._flowobj.isBreak():
                    self.debugPrint(f"loop break, last times: {self._times}")
                    return self._outnode
                self._flowobj = None
        else:
            self._times -= 1
        if self._times <= 0:
            # 循环结束就返回下一个
            self.debugPrint(f"loop finish")
            return self._outnode
        else:
            # 继续循环
            return self

class Node_Print(ExecuteNode):

    _name = "Print"

    def __init__(self, parent, uuid, info):
        super().__init__(parent, uuid, info)
        self._txt = info.get("Param", "")

    def onExecute(self):
        self.debugPrint(self._txt)
        return self._outnode

"""
如果在循环中想终止的话，就调用这个Node即可
如果不在循环里，将不会有任何事情发生
"""
class Node_BreakLoop(ExecuteNode):

    _name = "Break"

    def __init__(self, parent, uuid, info):
        super().__init__(parent, uuid, info)

    def onStarted(self):
        self.debugPrint(f"break loop")
        return super().onStarted()
    
    def onExecute(self):
        # 只要返回的不是自身就代表更换节点了
        self._parent.triggerBreak()
        return None

g_NodeCls = {
    "N_SLEEP": Node_Sleep,
    "N_FOR": Node_For,
    "N_BREAKLOOP": Node_BreakLoop,
    "N_PRINT": Node_Print,
}

def getNodeCls(nodetype):
    return g_NodeCls.get(nodetype, ExecuteNode)

"""
Entry : 流程入口，必须有
Out   : 每一个动作节点的出口，IF特殊有两个，分别是True,False
IF    : 条件节点

该数据结构仅描述流程与参数声明方面的定义，执行定义在具体action内部处理
"流程临时uuid" : {
    "Type": [Entry|End|IF|FOR|ACTION],
    "Action": "对应应该要执行的actionid/flowid",
    "Out": "[出口] Next uuid or END",
    "True": "[出口] IF 节点才有的出口",
    "False": "[出口] IF 节点才有的出口",
}
"""
class FlowBase:

    def __init__(self, filepath):
        f = open(filepath, "r", encoding="utf8")
        jsondata = f.read()
        try:
            data = json.loads(jsondata)
        except:
            DebugPrint(f"{filepath} load error.")
            data = {}
        self._filepath = filepath
        self._nodes = {}
        self._name = data.get("Name", "Untitle")
        self._flow = data.get("Flow", {})
        self._rootnode = None
        self._entrynode = None
        self._begin: bool = False
        self._breaktrigger: bool = False
        for nodeid, info in self._flow.items():
            cls = getNodeCls(info["Type"])
            node = cls(self, nodeid, info)
            DebugPrint("node load: ", nodeid, node)
            if nodeid in self._nodes:
                raise Exception(f"node \"{nodeid}\" already exists.")
            self._nodes[nodeid] = node
            if "Entry" == info["Type"]:
                self._rootnode = node
                self._entrynode = node
        for node in self._nodes.values():
            node.init()
    
    def create(self):
        import copy
        return copy.deepcopy(self)

    def getName(self):
        return self._name
    
    def getNode(self, nodeid):
        if not nodeid in self._nodes:
            return None
        return self._nodes[nodeid]

    def isBreak(self):
        return self._breaktrigger
    
    def triggerBreak(self):
        self._breaktrigger = True

    def execute(self):
        if not self._rootnode:
            # 执行完了，结束
            self.finish()
            return True
        # 执行Node, 执行完会返回下一个node
        if not self._begin:
            self._begin = True
            DebugPrint(f"ready execute flow: {self.getName()}")
        if not self._rootnode.isStarted():
            self._rootnode.start()
        node = self._rootnode.execute()
        if node != self._rootnode:
            self._rootnode.end()
        self._rootnode = node
        return False
    
    def reset(self):
        self._rootnode = self._entrynode
        for node in self._nodes.values():
            node.reset()
    
    def finish(self):
        self._rootnode = None
        DebugPrint(f"Flow \"{self._name}\" 执行完成.")

    def isFinished(self):
        if self._rootnode:
            return False
        return True

class FlowManager(common.globaldefines.ThreadBase):
    
    _name = "Flow"

    def __init__(self):
        super(FlowManager, self).__init__()
        self._flows = {}
        self._curflow: FlowBase = None
        global g_FlowManager
        g_FlowManager = self

    def getFlowName(self, flowid):
        if flowid not in self._flows:
            return "UnkonwFlow"
        return self._flows[flowid].getName()

    def onStarted(self):
        #actionmanager.g_ActMgr.LoadAllActions()
        self.loadAllFlows()
        application.g_App.startTimer(0.03, self.updateFrame)

    def onHandle(self, op, *args):
        DebugPrint(op, *args)
        if "ExecuteFlow" == op:
            self.executeFlow(args[0])
        return super().onHandle(op, *args)

    def updateFrame(self):
        self.stepExecute()
        application.g_App.startTimer(0.03, self.updateFrame)

    def getAllFlows(self):
        l = []
        for flagname, flowobj in self._flows.items():
            s = (flowobj.getName(), flagname)
            l.append(s)
        return l

    def loadAllFlows(self):
        import os
        s = os.path.abspath(__file__)
        s = os.path.join(os.path.dirname(s), "flows")
        if not os.path.exists(s):
            s = os.path.join(os.getcwd(), "flows")
        for root, _, files in os.walk(s):
            for filename in files:
                conffile = os.path.join(root, filename)
                if not os.path.exists(conffile):
                    continue
                flowobj = FlowBase(conffile)
                flagname = filename.replace(".json", "")
                DebugPrint(f"load flow [{flagname}]-{flowobj.getName()}")
                self._flows[flagname] = flowobj
            break

    def getFlow(self, flowid: str):
        return self._flows.get(flowid, None)
    
    def stepExecute(self):
        if not self._curflow:
            return
        r = self._curflow.execute()
        if r:
            self._curflow = None
            DebugPrint("Flow execute finished.")

    def executeFlow(self, flowid: str):
        flowobj = self.getFlow(flowid)
        if not flowobj:
            DebugPrint(f"flow \"{flowid}\" not found.")
            return
        self._curflow = flowobj.create()
        DebugPrint(f"Flow \"{flowid}\" \"{flowobj.getName()}\" start execute.") 

g_FlowManager = FlowManager()

if "__main__" == __name__:
    g_FlowManager.LoadAllActions()

    f = FlowBase("flows/flow2372-3489-472c-a47d-ea04987510b0.json")
    f.execute()
    f.execute()
    f.execute()